using System;
namespace simulation
{
	public class NBodySystem : IMechanicalSystem
	{
		public double [] m_;
		public double [] x_;
		public double [] y_;
		public double [] vx_;
		public double [] vy_;
		public int N_;
		public double time_;
		
		public NBodySystem (double []m, double [] x, double []y, double []vx, double []vy) 
		{
			N_ = m.Length;
			m_ = new double[N_];
			x_ = new double[N_];
			y_ = new double[N_];
			vx_ = new double[N_];
			vy_ = new double[N_];
			
			for (int i = 0; i < N_; i++) {
				m_[i] = m[i];
				x_[i] = x[i];
				vx_[i] = vx[i];
				y_[i] = y[i];
				vy_[i] = vy[i];
			}
		}

		public NBodySystem ()
		{
		}
		
		public object Clone()
		{
			return new NBodySystem(m_, x_, y_, vx_, vy_);
		}

		public void toCanonical(ref CanonicalSystem s)
		{
			int k = 0;
			for (int i = 0; i < N_; i++) {
				s.q_[k++] = x_[i];
				s.q_[k++] = y_[i];
			}
			for (int i = 0; i < N_; i++) {
				s.q_[k++] = vx_[i];
				s.q_[k++] = vy_[i];
			}
		}
		
		public void createCanonical(out CanonicalSystem s)
		{
			s = new CanonicalSystem (N_*4);
			toCanonical(ref s);
		}
		
		public void fromCanonical(CanonicalSystem s)
		{
			int k = 0;
			for (int i = 0; i < N_; i++) {
				x_[i] = s.q_[k++];
				y_[i] = s.q_[k++];
			}
			for (int i = 0; i < N_; i++) {
				vx_[i] = s.q_[k++];
				vy_[i] = s.q_[k++];
			}
		}
		
		public override string ToString ()
		{
			 return "x = " + x_.ToString() + " y = " + y_.ToString() +
				" vx = " + vx_.ToString() + " vy = " + vy_.ToString();
		}
		
		public void draw (Gdk.Window win, double x_unit, double y_unit)
		{
			int width, height;			

			Gdk.GC gc = new Gdk.GC (win);
	
			//win.ClearArea(clear, true);
			win.Clear ();
			win.GetSize (out width, out height);			
	
//			for (int i = 0; i < N_; i++) 
//				win.DrawArc(gc, true, width / 2 + (int)(x_[i] * x_unit), height / 2 - (int) (y_[i] * y_unit), 5, 5, 0, 360*64); // full circle
			
			double max = 0;
			for (int i = 0; i < N_-1; i++) 
			{
				double len = Math.Sqrt ((x_[i] - x_[i+1])*(x_[i] - x_[i+1]) + (y_[i]-y_[i+1])*(y_[i]-y_[i+1]));
				max = Math.Max(max, len);
			}
			
			byte red_intensity = 255;
			red_intensity = 255;
			Gdk.Color new_color = new Gdk.Color (red_intensity, 0, 0);
			Gdk.Colormap.System.AllocColor(ref new_color, true, true);

			int size = 5;
			for (int i = 0; i < N_-1; i++) {
				int x_i = width / 2 + (int)(x_[i] * x_unit);
				int y_i = height / 2 - (int)(y_[i] * y_unit);
				int x_i1 = width / 2 + (int)(x_[i+1] * x_unit);
				int y_i1 = height / 2 - (int)(y_[i+1] * y_unit);
				
				double len = Math.Sqrt ((x_[i] - x_[i+1])*(x_[i] - x_[i+1]) + (y_[i]-y_[i+1])*(y_[i]-y_[i+1]));

//				red_intensity = (byte) (len/max*255);
//				Gdk.Color red_color = new Gdk.Color ((byte)(red_intensity/3), (byte)(red_intensity/2), red_intensity);
//				Gdk.Colormap colormap = Gdk.Colormap.System;
//				colormap.AllocColor(ref red_color, true, true);
//				gc.Foreground = red_color;
//				gc.SetLineAttributes (2, Gdk.LineStyle.Solid, Gdk.CapStyle.Round, Gdk.JoinStyle.Round);
//				win.DrawLine(gc, x_i, y_i, x_i1, y_i1);
				
				gc.Foreground = new_color;
				win.DrawArc(gc, true, x_i1-size, y_i1-size, size*2+1, size*2+1, 0, 360*64); // full circle
			}
			
			gc.Foreground = new_color;
			win.DrawArc(gc, true, width / 2 + (int)(x_[0] * x_unit)-size, height / 2 - (int) (y_[0] * y_unit)-size, size*2+1, size*2+1, 0, 360*64); // full circle
		}

		public void clear(Gdk.Window win, double x_unit, double y_unit)
		{
			int width, height;
			win.GetSize(out width, out height);
			for (int i = 0; i < N_; i++) 
			{
				int x_i = width / 2 + (int)(x_[i] * x_unit);
				int y_i = height / 2 - (int) (y_[i] * y_unit);
				win.ClearArea(x_i-7, y_i-7, 15, 15);
			}
		}
		
	}

	class SpringForceNBody : Force<NBodySystem>
	{
		public override void getDot(NBodySystem s, ref double[] qdot)
		{
			int k = 0;
			for (int i = 0; i < s.N_; i++) {
				qdot[k++] = s.vx_[i];
				qdot[k++] = s.vy_[i];
			}

			double K = 10000;
			int k_tmp = k;
			
			for (int i = 0; i < s.N_; i++) {
				qdot[k_tmp++] = 0;
				qdot[k_tmp++] = 0;
			}
			
			for (int i = 0; i < s.N_-1; i++,k+=2) {
				double forcey = K * (s.y_[i] - s.y_[i+1]);
				double forcex = K * (s.x_[i] - s.x_[i+1]);
				qdot[k] += -forcex / s.m_[i];
				qdot[k+1] += -forcey / s.m_[i];
				qdot[k+2] += forcex / s.m_[i+1];
				qdot[k+3] += forcey / s.m_[i+1];
			}
		}
	}

	class GravityForceNBody : Force<NBodySystem>
	{
		public double G = .1;
		public GravityForceNBody(double G)
		{
			this.G = G;
		}
		
		public override void getDot(NBodySystem s, ref double[] qdot)
		{
			int k = 0;
			for (int i = 0; i < s.N_; i++) {
				qdot[k++] = s.vx_[i];
				qdot[k++] = s.vy_[i];
			}

			int k_tmp = k;
			
			for (int i = 0; i < s.N_; i++) {
				qdot[k_tmp++] = 0;
				qdot[k_tmp++] = 0;
			}
			
			for (int i = 0; i < s.N_-1; i++)
				for (int j = i+1; j < s.N_; j++)
			{
				double dx = s.x_[i] - s.x_[j];
				double dy = s.y_[i] - s.y_[j];
				
				double r3 = Math.Pow(dx * dx + dy * dy, 1.5);
				
				double force = s.m_[i] * s.m_[j] * this.G / r3;
				qdot[k+i*2] += -force * dx / s.m_[i];
				qdot[k+i*2+1] += -force * dy / s.m_[i];
				qdot[k+j*2] += force *dx / s.m_[j];
				qdot[k+j*2+1] += force * dy / s.m_[j];
			}
		}
	}

}

